李昂・巴蒂斯塔・阿伯提(Leon Battista Alberti)在《論建築》裡提出 concinnitas
各部分之間的和諧比例,並給「美」下了近乎嚴苛的定義:「美,是所有部分之間的和諧與一致,達到一種境界——多加一分是累贅,少了一分是缺憾」
模組一到模組三,處理的都是「結構性」問題
模組四不一樣,處理的是「贅肉」,那些不影響功能,卻默默增加維護成本的多餘之物
六個「贅肉」警訊:
| Day | Code Smell | 一句話定位 |
|---|---|---|
| 19 | 註解 (Comments) | 用一段解釋,掩蓋一段本該寫清楚的程式碼 |
| 20 | 重複的程式碼 (Duplicate Code) | 同一段邏輯,在系統裡被抄寫了不只一次 |
| 21 | 懶惰的類別 (Lazy Class) | 存在的價值,撐不起維護它的成本 |
| 22 | 純資料類別 (Data Class) | 只會被別人擠資料,自己什麼都不做 |
| 23 | 無用的程式碼 (Dead Code) | 沒有人呼叫,卻沒有人敢刪的舊程式碼 |
| 24 | 猜測性通用 (Speculative Generality) | 為了「以後可能會用到」預先蓋好的空彈性 |
接下來六天,練習的不是「加什麼」,是 「什麼可以拿掉」
第一站,從最容易被誤解的一種開始
「註解」
一幅畫,如果畫得夠好,不需要在畫框上貼一張紙條寫「這裡是一朵花」
觀者一眼就看得出來,那是一朵花
因為花瓣的形狀、顏色的層次、光影的處理,已經把「這是花」這件事,說得很清楚了
如果一幅畫,必須靠貼紙條才能讓人看懂畫的是什麼
問題不在紙條寫得好不好,是那幅畫,本來就沒畫清楚
團隊要加一個「免運門檻」判斷,寫出來的第一版是這樣:
public bool IsEligibleForFreeShipping(OrderRequest request)
{
// 如果是 VIP 客戶,直接享有免運
// 如果訂單金額超過 1000 元,也享有免運
// 但如果是超商取貨,門檻降到 500 元,且不能是易碎品
if (request.Tier == CustomerTier.Vip ||
request.Line.Qty * 100 >= 1000 ||
(request.DeliveryMethod == "超商" && request.Line.Qty * 100 >= 500 && !request.IsFragile))
{
return true;
}
return false;
}
三行註解,對應著三條規則
看起來很貼心不懂的人,讀註解就知道邏輯是什麼
半年後,業務調整規則:超商取貨的免運門檻,從 500 元調成 700 元
工程師改了程式碼裡的 500,改成 700
改完程式碼,順手忘記改註解,註解上還寫著「門檻降到 500 元」
三個月後,另一位工程師讀到這段程式碼,先看了註解,以為門檻是 500,改了另一段依賴這個門檻的邏輯
結果兩處數字對不上,產生了一個要花一個下午才查得出來的 Bug
這正是註解最危險的地方
「程式碼會被執行,註解不會。程式碼錯了,測試會抓到;註解錯了,沒有任何機制會提醒你」
真正的問題,不是「該不該寫註解」,是這段判斷式,本來就沒有把自己的意圖說清楚
解法是提煉變數 (Extract Variable):把每一段條件,命名成一個有意義的布林變數,讓變數名稱取代原本的註解
public bool IsEligibleForFreeShipping(OrderRequest request)
{
bool isVip = request.Tier == CustomerTier.Vip;
bool meetsStandardThreshold = request.Line.Qty * 100 >= 1000;
bool meetsConvenienceStoreThreshold =
request.DeliveryMethod == "超商" &&
request.Line.Qty * 100 >= 700 &&
!request.IsFragile;
return isVip || meetsStandardThreshold || meetsConvenienceStoreThreshold;
}
三個變數名稱,就是三條規則的說明書
門檻從 500 改成 700,只需要改一個地方,不會有「程式碼跟註解對不上」的風險
因為現在只有一份真相,不是程式碼加上一份平行的文字說明
阿伯提的節制美學,不是「所有裝飾都不該有」,是每一個留下的裝飾,都該有不可替代的理由
註解也一樣,有幾種情況,它是真正必要的:
「做什麼」的註解該被消滅;「為什麼」的註解,才值得留下
明天我們看一種更直接的贅肉:同一段邏輯,被複製貼上到系統的好幾個角落,各自過著自己的人生
模組四第二站:重複的程式碼(Duplicate Code)